﻿#include "formrealtimeplot.h"
#include "ui_formrealtimeplot.h"
#include "datasimucache.h"
#include "globalstatic.h"
#include "globalsymbol.h"

#include <QDebug>
#include <QVBoxLayout>
#include <QApplication>
#include <QDir>
#include <QFile>
#include <QCollator>

FormRealtimePlot::FormRealtimePlot(QSettings* settings, JsonConfig* jsonConfig, QWidget *parent) :
    QWidget(parent),
    ui(new Ui::FormRealtimePlot),
    m_settings(settings),
    m_jsonConfig(jsonConfig)
{
    ui->setupUi(this);

    // 初始化
    initUI();
    initPlot();
    init();
}

FormRealtimePlot::~FormRealtimePlot()
{
    delete ui;
}

void FormRealtimePlot::setChannelCoefficient(const QList<double> &coefficientList)
{
    for (int i = 0; i < coefficientList.size(); i++) {
        QString id = QString("%1").arg(i, 3, 10, QLatin1Char('0'));
        m_channelCoefficientMap[id] = coefficientList.at(i);
    }
}

void FormRealtimePlot::updatePlot(const QList<QPair<QString, QString>> &pairList)
{
    // 清空通道列表(通道ID和图表曲线一一对应)
    m_channels.clear();
    // 删除图表的全部曲线
    m_plot->clearGraphs();

    for (int i = 0; i < pairList.size(); i++) {
        // 添加通道(通道ID和图表曲线一一对应)
        m_channels << pairList.at(i).first;
        // 添加数据曲线
        m_plot->addGraph();
        m_plot->graph(i)->setName(pairList.at(i).second);

        QPen graphPen(m_jsonConfig->graphProperty(pairList.at(i).first).color, 1, Qt::PenStyle::SolidLine);
        m_plot->graph()->setPen(graphPen);
    }
    if (pairList.empty()) {
        // 添加1条未命名曲线以调整因图例引起的布局
        m_plot->addGraph();
        m_plot->graph()->setName("");
    }

    // X轴计数
    m_xValue = 0;

    // 刷新图表
    m_plot->replot(QCustomPlot::rpQueuedReplot);
}

void FormRealtimePlot::clear(bool onlyData)
{
    // X轴, Y轴坐标范围
    m_plot->xAxis->setRange(0, m_xSize);
    m_plot->yAxis->setRange(m_jsonConfig->plotYMin(), m_jsonConfig->plotYMax());
    if (onlyData) {
        // 曲线数据
        int graphCount = m_plot->graphCount();
        for (int i = 0; i < graphCount; i++) {
            m_plot->graph(i)->data()->clear();
        }
    } else {
        // 清空通道列表(通道ID和图表曲线一一对应)
        m_channels.clear();
        // 删除图表的全部曲线
        m_plot->clearGraphs();
        // 添加1条未命名曲线以调整因图例引起的布局
        m_plot->addGraph();
        m_plot->graph()->setName("");
    }
    // X轴计数
    m_xValue = 0;

    // 刷新图表
    m_plot->replot(QCustomPlot::rpQueuedReplot);
}

void FormRealtimePlot::showLastPlot(const QList<ChannelDescription> &channelDescriptionList)
{
    if (m_channels.empty()) {
        QMessageBox::information(this, QStringLiteral("提示"), QStringLiteral("未勾选通道或显示曲线!"));
        return;
    }

    QString dataFileName;
    if (!findLastFile(dataFileName)) {
        QMessageBox::information(this, QStringLiteral("提示"), QStringLiteral("未查询到最近的历史数据文件!"));
        return;
    }

    // 读取二进制原始数据文件
    QList<QByteArray> bufferList;
    readDataFile(dataFileName, bufferList);
    if (bufferList.empty()) {
        QMessageBox::information(this, QStringLiteral("提示"), QStringLiteral("原始数据文件为空!"));
        return;
    }

    // 提取文件中的通道数值, 曲线顺序和勾选的通道一一对应
    QList<QVariantList> valueList;
    fetchChannelValue(channelDescriptionList, bufferList, valueList);

    // 刷新曲线数据(通道ID和图表曲线一一对应), 刷新前先情况原有数据
    int graphCount = m_plot->graphCount();
    for (int i = 0; i < graphCount; i++) {
        m_plot->graph(i)->data()->clear();
    }
    for (int i = 0; i < valueList.size(); i++) {
        for (int j = 0; j < graphCount; j++) {
            QString id = m_channels.at(j);
            double yValue = valueList.at(i).at(j).toDouble() * m_channelCoefficientMap.value(id);
            m_plot->graph(j)->addData(i, yValue);
        }
    }
    m_plot->replot(QCustomPlot::rpQueuedReplot);
}

void FormRealtimePlot::initUI()
{
    // 装载图表曲线的控件
    m_wgtPlot = new QWidget(this);
    m_wgtPlot->setObjectName("wgtPlot");

    // 垂直布局
    QVBoxLayout* vboxLayout = new QVBoxLayout(this);
    vboxLayout->setContentsMargins(0, 0, 0, 0);
    vboxLayout->addWidget(m_wgtPlot);
}

void FormRealtimePlot::initPlot()
{
    // 垂直布局
    QVBoxLayout *plotLayout = new QVBoxLayout(m_wgtPlot);
    plotLayout->setContentsMargins(0, 0, 0, 0);
    plotLayout->setSpacing(0);
    m_plot = new QCustomPlot(m_wgtPlot);
    m_plot->setObjectName("m_plot");
    plotLayout->addWidget(m_plot);
    // 设置背景网格线是否显示
    m_plot->xAxis->grid()->setVisible(true);
    m_plot->yAxis->grid()->setVisible(true);
    // 坐标轴的范围和标签
    m_plot->axisRect()->setupFullAxesBox();
    m_plot->xAxis->setRange(0, m_jsonConfig->plotXSize());
    m_plot->yAxis->setRange(m_jsonConfig->plotYMin(), m_jsonConfig->plotYMax());
    m_plot->xAxis->setLabel(QStringLiteral("点数"));
    m_plot->yAxis->setLabel(QStringLiteral("数值"));
    // 设置坐标轴2隐藏, 把画笔置为Qt::NoPen达到隐藏轴的效果
    m_plot->xAxis2->setTicks(false);
    m_plot->yAxis2->setTicks(false);
    m_plot->xAxis2->setBasePen(QPen(Qt::NoPen));
    m_plot->yAxis2->setBasePen(QPen(Qt::NoPen));

    // 添加1条未命名曲线以调整因图例引起的布局
    m_plot->addGraph();
    m_plot->graph()->setName("");

    // 下方显示图例, 隐藏边框, 行优先排列, 6个图例自动换行
    m_plot->legend->setVisible(true);
    m_plot->legend->setBorderPen(Qt::NoPen);
    m_plot->legend->setFillOrder(QCPLayoutGrid::foColumnsFirst);
    m_plot->legend->setWrap(6);
    m_plot->plotLayout()->addElement(1, 0, m_plot->legend);
    m_plot->plotLayout()->setRowStretchFactor(1, 0.01);
    m_plot->plotLayout()->setRowSpacing(10);
    //m_plot->axisRect()->insetLayout()->setInsetAlignment(0, Qt::AlignTop | Qt::AlignRight);

    // 关闭抗锯齿以及设置拖动的时候不启用抗锯齿
    m_plot->graph()->setAntialiased(false);
    m_plot->setNoAntialiasingOnDrag(true);

    // 缩放: 滚轮滑动缩放
    //m_plot->setInteractions(QCP::iRangeZoom | QCP::iSelectAxes | QCP::iSelectPlottables);
    m_plot->setInteractions(QCP::iSelectAxes | QCP::iSelectPlottables);
    // 选框样式: 虚线, 半透明浅蓝
    m_plot->selectionRect()->setPen(QPen(Qt::lightGray, 1, Qt::DashLine));
    m_plot->selectionRect()->setBrush(QBrush(QColor(192, 192, 216, 100)));
    // 框选放大
    m_plot->setSelectionRectMode(QCP::SelectionRectMode::srmZoom);

    // 右键菜单
    m_plot->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(m_plot, &QCustomPlot::customContextMenuRequested, this, &FormRealtimePlot::onPlotContextMenu);
}

void FormRealtimePlot::init()
{
    // X轴的范围限制
    m_xSize = m_jsonConfig->plotXSize();
    // X轴计数
    m_xValue = 0;
    // 曲线数据刷新时间控制
    m_refreshTime = QTime::currentTime();

    // 存盘路径
    m_settings->beginGroup("Storage");
    QString savePath = m_settings->value("savePath").toString();
    m_settings->endGroup();
    m_savePath = savePath.prepend(qApp->applicationDirPath());
}

bool FormRealtimePlot::findLastFile(QString &dataFileName)
{
//    QStringList history;

//    // 找到存储路径下的所有dat文件
//    QDir dir(m_savePath);
//    QStringList filter;
//    filter << "*.dat";
//    dir.setNameFilters(filter);
//    QFileInfoList fileInfoList = dir.entryInfoList(filter);
//    for (int i = 0; i < fileInfoList.size(); i++) {
//        QString fileName = fileInfoList.at(i).fileName();
//        if (fileName.startsWith("data"))
//            history << fileName;
//    }

//    if (history.empty())
//        return false;

//    // 倒序排列
//    QCollator sorter;
//    sorter.setNumericMode(true);
//    sorter.setCaseSensitivity(Qt::CaseInsensitive);
//    std::sort(history.begin(), history.end(), [&](const QString& a, const QString b) { return sorter.compare(a, b) > 0; });
//    fileName = history.first();

//    return true;

    // 根据时间找到最新的数据文件
    m_settings->beginGroup("Storage");
    QString savePath = m_settings->value("savePath").toString();
    QString lastest = m_settings->value("lastest").toString();
    m_settings->endGroup();
    dataFileName = QString::fromUtf8("%1%2/data_%3.dat").arg(qApp->applicationDirPath(), savePath, lastest);
    //qDebug() << "**********dataFileName: " << dataFileName;

    QFileInfo dataFileInfo(dataFileName);

    return dataFileInfo.exists();
}

void FormRealtimePlot::readDataFile(const QString &filename, QList<QByteArray> &bufferList)
{
    QFile dataFile(filename);
    if (!dataFile.open(QIODevice::ReadOnly))
        return;

    // 使用二进制方式读取文件
    qint64 size = dataFile.size();
    qint32 dataLength = GlobalStatic::getDataLength();
    for (qint64 i = 0; i < size; i += dataLength) {
        QByteArray buffer(dataLength, 0x0);
        dataFile.seek(i);
        dataFile.read(buffer.data(), dataLength);
        bufferList << buffer;
    }
    dataFile.close();
}

void FormRealtimePlot::fetchChannelValue(const QList<ChannelDescription> &channelDescriptionList, const QList<QByteArray> &bufferList, QList<QVariantList> &valueList)
{
    // 曲线顺序和勾选的通道一一对应
    for (int i = 0; i < bufferList.size(); i++) {
        // 通道数值列表
        QVariantList varList;

        QByteArray buffer = bufferList.at(i);
        int bufferIndex = 0;

        // 时间戳
        qint64 timeStamp = 0;
        memcpy(&timeStamp, buffer.constData(), sizeof(qint64));
        bufferIndex += sizeof(qint64);

        for (int i = 0; i < channelDescriptionList.size(); i++) {
            DataType dataType = GlobalStatic::getDataType(channelDescriptionList.at(i).dataType);
            switch (dataType) {
            case UC:
                if (m_channels.contains(channelDescriptionList.at(i).id))
                    varList << static_cast<unsigned char>(buffer.at(bufferIndex));
                bufferIndex += sizeof(unsigned char);
                break;
            case SC:
                if (m_channels.contains(channelDescriptionList.at(i).id))
                    varList << buffer.at(bufferIndex);
                bufferIndex += sizeof(char);
                break;
            case US: {
                if (m_channels.contains(channelDescriptionList.at(i).id)) {
                    int value = ((buffer.at(bufferIndex) & 0xFF) << 8) | (buffer.at(bufferIndex + 1) & 0xFF);
                    varList << value;
                }
                bufferIndex += sizeof(unsigned short);
            }
                break;
            case SS: {
                if (m_channels.contains(channelDescriptionList.at(i).id)) {
                    int value = ((buffer.at(bufferIndex) & 0xFF) << 8) | (buffer.at(bufferIndex + 1) & 0xFF);
                    varList << value;
                }
                bufferIndex += sizeof(short);
            }
                break;
            case UI: {
                if (m_channels.contains(channelDescriptionList.at(i).id)) {
                    int value = ((buffer.at(bufferIndex) & 0xFF) << 24) | ((buffer.at(bufferIndex + 1) & 0xFF) << 16) | ((buffer.at(bufferIndex + 2) & 0xFF) << 8) | (buffer.at(bufferIndex + 3) & 0xFF);
                    varList << value;
                }
                bufferIndex += sizeof(unsigned int);
            }
                break;
            case SI: {
                if (m_channels.contains(channelDescriptionList.at(i).id)) {
                    int value = ((buffer.at(bufferIndex) & 0xFF) << 24) | ((buffer.at(bufferIndex + 1) & 0xFF) << 16) | ((buffer.at(bufferIndex + 2) & 0xFF) << 8) | (buffer.at(bufferIndex + 3) & 0xFF);
                    varList << value;
                }
                bufferIndex += sizeof(int);
            }
                break;
            case UL: {
                if (m_channels.contains(channelDescriptionList.at(i).id)) {
                    unsigned long long value = 0;
                    memcpy(&value, buffer.constData() + bufferIndex, sizeof(unsigned long long));
                    varList << value;
                }
                bufferIndex += sizeof(unsigned long long);
            }
                break;
            case SL: {
                if (m_channels.contains(channelDescriptionList.at(i).id)) {
                    long long value = 0;
                    memcpy(&value, buffer.constData() + bufferIndex, sizeof(long long));
                    varList << value;
                }
                bufferIndex += sizeof(long long);
            }
                break;
            case F32:
            case F: {
                if (m_channels.contains(channelDescriptionList.at(i).id)) {
                    float value = 0;
                    memcpy(&value, buffer.constData() + bufferIndex, sizeof(float));
                    varList << value;
                }
                bufferIndex += sizeof(float);
            }
                break;
            case F64:
            case DBL: {
                if (m_channels.contains(channelDescriptionList.at(i).id)) {
                    double value = 0;
                    memcpy(&value, buffer.constData() + bufferIndex, sizeof(double));
                    varList << value;
                }
                bufferIndex += sizeof(double);
            }
                break;
            default:
                break;
            }
        }

        valueList << varList;
    }
}

void FormRealtimePlot::onPlotContextMenu(const QPoint &pos)
{
    QMenu* menu = new QMenu(this);
    menu->setAttribute(Qt::WA_DeleteOnClose);
    QAction* action = new QAction(QStringLiteral("恢复默认"), nullptr);
    connect(action, &QAction::triggered, this, [=]() {
        // 恢复默认的坐标轴范围
        m_plot->xAxis->setRange(0, m_xSize);
        m_plot->yAxis->setRange(m_jsonConfig->plotYMin(), m_jsonConfig->plotYMax());

        // 刷新图表
        m_plot->replot(QCustomPlot::rpQueuedReplot);
    });
    menu->addAction(action);
    menu->popup(m_plot->mapToGlobal(pos));
}

void FormRealtimePlot::onRefreshPlot()
{
    // X轴计数
    m_xValue++;

    if (m_channels.empty())
        return;

    // 刷新曲线数据(通道ID和图表曲线一一对应)
    int graphCount = m_plot->graphCount();
    for (int i = 0; i < graphCount; i++) {
        QString id = m_channels.at(i);
        if (DataSimuCache::contains(id)) {
            double yValue = DataSimuCache::getSimuValue(id).toDouble() * m_channelCoefficientMap.value(id);
            m_plot->graph(i)->addData(m_xValue, yValue);
            m_plot->graph(i)->removeDataBefore(m_xValue - m_xSize);
            if (m_xValue > m_xSize)
                m_plot->xAxis->setRange(m_xValue - m_xSize, m_xValue);
        }
    }

    // 200ms刷新1次
    if(QTime::currentTime() > m_refreshTime.addMSecs(200)) {
        m_refreshTime = QTime::currentTime();
        m_plot->replot(QCustomPlot::rpQueuedReplot);
    }

}
